/* 
 * Cashbox is the legal property of its developers, whose names are listed in the copyright file included
 * with this source distribution.
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
 * Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program; if not,
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#import "CBSavingsTest.h"
#import "CBTestMacros.h"
#import "CBSavingsTransaction.h"
#import <Cashbox/CBAccount.h>
#import <Cashbox/CBDataControllerProtocol.h>
#import <Cashbox/CBAccountControllerProtocol.h>
#import <Cashbox/CBAccountExceptions.h>

@implementation CBSavingsTest

- (void)testCreateAndDestroy { 
	CBUsingPluginAccount(CBSavingsAccount);
	CBAccount *account = [[CBSavingsAccount alloc] init];
	[account setName:@"Test account"];
	[[cashbox dataController] saveData:nil];
	STAssertNotNil(account, @"account could not be created");
	[[[cashbox dataController] managedObjectContext] deleteObject:account];
	STAssertTrue([account isDeleted], @"account could not be deleted");
	[[cashbox dataController] saveData:nil];
}

- (void)testTransactions {
	CBUsingPluginAccount(CBSavingsAccount);
	CBAccount *account = [[CBSavingsAccount alloc] init];
	[account setName:@"Test account"];
	[[cashbox dataController] saveData:nil];	
	Class transactionClass = [CBSavingsAccount transactionClass];
	CBSavingsTransaction *t = [[transactionClass alloc] initAndInsertIntoAccount:account];
	[t setDate:[NSDate dateWithString:@"2008-05-20 00:00:00 +0000"]];
	[[cashbox dataController] saveData:nil];
	STAssertNotNil(t, @"transaction could not be added");
	[[[cashbox dataController] managedObjectContext] deleteObject:t];
	STAssertTrue([t isDeleted], @"transaction could not be deleted");
	[[cashbox dataController] saveData:nil];
	CBSavingsTransaction *d1 = [[transactionClass alloc] initAndInsertIntoAccount:account];
	[d1 setDate:[NSDate dateWithString:@"2008-05-14 00:00:00 +0000"]];
	[[cashbox dataController] saveData:nil];
	STAssertNotNil(d1, @"transaction could not be added");
	[d1 setDeposit:(long long)20];
	[[cashbox dataController] saveData:nil];
	STAssertEquals([account total], (long long)20, @"Account total does not match transactions");
	STAssertEquals([d1 total], (long long)20, @"Transaction total not properly updated");
	CBSavingsTransaction *d2 = [[transactionClass alloc] initAndInsertIntoAccount:account];
	[d2 setDate:[NSDate dateWithString:@"2008-05-15 00:00:00 +0000"]];
	[[cashbox dataController] saveData:nil];
	STAssertNotNil(d2, @"transaction could not be added");
	[d2 setDeposit:(long long)50];
	[[cashbox dataController] saveData:nil];
	STAssertEquals([d2 total], (long long)70, @"Transaction total not properly updated");
	STAssertEquals([account total], (long long)70, @"Account total does not match transactions");	
	CBSavingsTransaction *w1 = [[transactionClass alloc] initAndInsertIntoAccount:account];
	[w1 setDate:[NSDate dateWithString:@"2008-05-16 00:00:00 +0000"]];
	[[cashbox dataController] saveData:nil];
	STAssertNotNil(w1, @"transaction could not be added");
	[w1 setWithdrawal:(long long)30];
	[[cashbox dataController] saveData:nil];
	STAssertEquals([w1 total], (long long)40, @"Transaction total not properly updated");
	STAssertEquals([account total], (long long)40, @"Account total does not match transactions");	
	CBSavingsTransaction *w2 = [[transactionClass alloc] initAndInsertIntoAccount:account];
	[w2 setDate:[NSDate dateWithString:@"2008-05-17 00:00:00 +0000"]];
	[[cashbox dataController] saveData:nil];
	STAssertNotNil(w2, @"transaction could not be added");
	[w2 setWithdrawal:(long long)80];
	[[cashbox dataController] saveData:nil];
	STAssertEquals([w2 total], (long long)-40, @"Transaction total not properly updated");
	STAssertEquals([account total], (long long)-40, @"Account total does not match transactions");	
	[[[cashbox dataController] managedObjectContext] deleteObject:w2];
	STAssertTrue([w2 isDeleted], @"transaction could not be deleted");
	STAssertEquals([account total], (long long)40,  @"Account total does not match transactions");	
	[[cashbox dataController] saveData:nil];

	CBSavingsTransaction *w3 = [[transactionClass alloc] init];
	[w3 setDate:[NSDate dateWithString:@"2008-05-10 00:00:00 +0000"]];
	[w3 setWithdrawal:(long long)30];
	[account addTransactionsObject:w3];
	STAssertEquals([w3 total], (long long)-30, @"Transaction total not properly updated");
	STAssertEquals([account total], (long long)10, @"Account total does not match transactions");

	STAssertEquals([d1 total], (long long)-10, @"Transaction total not properly updated");
	STAssertEquals([d2 total], (long long)40, @"Transaction total not properly updated");
	STAssertEquals([w1 total], (long long)10, @"Transaction total not properly updated");
	[[cashbox dataController] saveData:nil];
	
	[[[cashbox dataController] managedObjectContext] deleteObject:d1];
	STAssertTrue([d1 isDeleted], @"transaction could not be deleted");
	[[cashbox dataController] saveData:nil];
	[[[cashbox dataController] managedObjectContext] deleteObject:d2];
	STAssertTrue([d2 isDeleted], @"transaction could not be deleted");	
	[[cashbox dataController] saveData:nil];
	[[[cashbox dataController] managedObjectContext] deleteObject:w1];
	STAssertTrue([w1 isDeleted], @"transaction could not be deleted");	
	[[cashbox dataController] saveData:nil];
	[[[cashbox dataController] managedObjectContext] deleteObject:w3];
	STAssertTrue([w3 isDeleted], @"transaction could not be deleted");	
	[[cashbox dataController] saveData:nil];
	[[[cashbox dataController] managedObjectContext] deleteObject:account];	
	[[cashbox dataController] saveData:nil];
}

- (void)testCreateAndDeleteTransaction {
	CBUsingPluginAccount(CBSavingsAccount);
	CBAccount *account = [[CBSavingsAccount alloc] init];
	[account setName:@"Test account"];
	[[cashbox dataController] saveData:nil];	
	Class transactionClass = [CBSavingsAccount transactionClass];
	
	{
		CBSavingsTransaction *t = [[transactionClass alloc] initAndInsertIntoAccount:account];
		[[cashbox dataController] saveData:nil];
	}
	
	// reset everything so it seems like the app is staring up again
	[[[cashbox dataController] managedObjectContext] reset];

	{
		NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
		[request setEntity:[CBTransaction entity]];
		NSArray *results = [[[cashbox dataController] managedObjectContext] executeFetchRequest:request error:NULL];
		STAssertTrue([results count] == 1, @"More transactions than expected");
		
		CBSavingsTransaction *t = [results objectAtIndex:0];
		[[[cashbox dataController] managedObjectContext] deleteObject:t];
		STAssertTrue([t isDeleted], @"transaction could not be deleted");
		[[cashbox dataController] saveData:nil];
	}
}

- (void)testTransfers {
	CBUsingPluginAccount(CBSavingsAccount);
	CBAccount *account1 = [[CBSavingsAccount alloc] init];
	[account1 setName:@"Transfer test account"];

	CBAccount *account2 = [[CBSavingsAccount alloc] init];
	[account2 setName:@"Transfer test account 2"];

	Class transactionClass = [CBSavingsAccount transactionClass];
	 	
	NSDate *originalDate = [NSDate dateWithString:@"2008-05-20 00:00:00 +0000"];
	CBSavingsTransaction *t1 = [[transactionClass alloc] initAndInsertIntoAccount:account1];
	[t1 setDate:originalDate];
	[t1 setDeposit:(long long)20];
	
	CBSavingsTransaction *t2 = [[transactionClass alloc] initAndInsertIntoAccount:account2];

	[t2 setDate:[NSDate dateWithString:@"2008-09-20 00:00:00 +0000"]];
	STAssertThrowsSpecific(
		[t1 setTransfer:t2],
		CBInvalidTransferSetupException,
		@"Setting a transction as a transfer when it has data set should raise an exception"
	);
	[t2 setDate:nil];
	
	[t2 setWithdrawal:30];	
	STAssertThrowsSpecific(
		[t1 setTransfer:t2],
		CBInvalidTransferSetupException,
		@"Setting a transction as a transfer when it has data set should raise an exception"
	);
	[t2 setWithdrawal:0];
	
	[t2 setDeposit:30];
	STAssertThrowsSpecific(
		[t1 setTransfer:t2],
		CBInvalidTransferSetupException,
		@"Setting a transction as a transfer when it has data set should raise an exception"
	);
	[t2 setDeposit:0];
	
	[t2 setDescrip:@"some description"];
	STAssertThrowsSpecific(
		[t1 setTransfer:t2],
		CBInvalidTransferSetupException,
		@"Setting a transction as a transfer when it has data set should raise an exception"
	);
	[t2 setDescrip:nil];
	
	// allow setting the transaction now
	[t1 setTransfer:t2];

	STAssertEquals([t1 total], (long long)20, @"Transaction total should have remained the same");
	STAssertEquals([t1 internalTotal], (long long)20, @"Transaction internal total should have remained the same");
	STAssertEquals([t1 deposit], (long long)20, @"Transaction deposit  should have remained the same");
	STAssertEquals([t1 withdrawal], (long long)0, @"Transaction withdrawal should have remained the same");
	STAssertEquals([t1 date], originalDate, @"Transaction date should have remained the same");
	
	STAssertEquals([t2 total], (long long)-20, @"Transaction total not properly updated");
	STAssertEquals([t2 internalTotal], (long long)-20, @"Transaction internalTotal not properly updated");
	STAssertEquals([t2 deposit], (long long)0, @"Transaction deposit not properly updated");
	STAssertEquals([t2 withdrawal], (long long)20, @"Transaction withdrawal not properly updated");
	STAssertEquals([t2 date], originalDate, @"Transaction date not properly updated");
	
	[[cashbox dataController] saveData:nil];
	
	[t1 setDeposit:(long long)30];
	STAssertEquals([t1 total], (long long)30, @"Transaction total should have remained the same");
	STAssertEquals([t1 internalTotal], (long long)30, @"Transaction internal total should have remained the same");
	STAssertEquals([t1 deposit], (long long)30, @"Transaction deposit  should have remained the same");
	STAssertEquals([t1 withdrawal], (long long)0, @"Transaction withdrawal should have remained the same");
	
	STAssertEquals([t2 total], (long long)-30, @"Transaction total not properly updated");
	STAssertEquals([t2 internalTotal], (long long)-30, @"Transaction internalTotal not properly updated");
	STAssertEquals([t2 deposit], (long long)0, @"Transaction deposit not properly updated");
	STAssertEquals([t2 withdrawal], (long long)30, @"Transaction withdrawal not properly updated");
	
	[t2 setWithdrawal:(long long)5];
	STAssertEquals([t1 total], (long long)5, @"Transaction total should have remained the same");
	STAssertEquals([t1 internalTotal], (long long)5, @"Transaction internal total should have remained the same");
	STAssertEquals([t1 deposit], (long long)5, @"Transaction deposit  should have remained the same");
	STAssertEquals([t1 withdrawal], (long long)0, @"Transaction withdrawal should have remained the same");
	
	STAssertEquals([t2 total], (long long)-5, @"Transaction total not properly updated");
	STAssertEquals([t2 internalTotal], (long long)-5, @"Transaction internalTotal not properly updated");
	STAssertEquals([t2 deposit], (long long)0, @"Transaction deposit not properly updated");
	STAssertEquals([t2 withdrawal], (long long)5, @"Transaction withdrawal not properly updated");
	
	[t2 setDeposit:(long long)5];
	STAssertEquals([t1 total], (long long)0, @"Transaction total should have remained the same");
	STAssertEquals([t1 internalTotal], (long long)0, @"Transaction internal total should have remained the same");
	STAssertEquals([t1 deposit], (long long)5, @"Transaction deposit  should have remained the same");
	STAssertEquals([t1 withdrawal], (long long)5, @"Transaction withdrawal should have remained the same");
	
	STAssertEquals([t2 total], (long long)0, @"Transaction total not properly updated");
	STAssertEquals([t2 internalTotal], (long long)0, @"Transaction internalTotal not properly updated");
	STAssertEquals([t2 deposit], (long long)5, @"Transaction deposit not properly updated");
	STAssertEquals([t2 withdrawal], (long long)5, @"Transaction withdrawal not properly updated");
	
	[t1 setWithdrawal:(long long)10];
	STAssertEquals([t1 total], (long long)-5, @"Transaction total should have remained the same");
	STAssertEquals([t1 internalTotal], (long long)-5, @"Transaction internal total should have remained the same");
	STAssertEquals([t1 deposit], (long long)5, @"Transaction deposit  should have remained the same");
	STAssertEquals([t1 withdrawal], (long long)10, @"Transaction withdrawal should have remained the same");
	
	STAssertEquals([t2 total], (long long)5, @"Transaction total not properly updated");
	STAssertEquals([t2 internalTotal], (long long)5, @"Transaction internalTotal not properly updated");
	STAssertEquals([t2 deposit], (long long)10, @"Transaction deposit not properly updated");
	STAssertEquals([t2 withdrawal], (long long)5, @"Transaction withdrawal not properly updated");

	[[cashbox dataController] saveData:nil];

	NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
	[request setEntity:[CBTransaction entity]];
	NSArray *results = [[[cashbox dataController] managedObjectContext] executeFetchRequest:request error:NULL];
	STAssertTrue([results count] == 2, @"More transactions than expected");
	
	CBSavingsTransaction *fetch1 = [results objectAtIndex:0];
	CBSavingsTransaction *fetch2 = [results objectAtIndex:0];
	
	[t1 setDeposit:500];
	STAssertEquals([t2 withdrawal], (long long)500, @"Update not processed");

	[t2 setDeposit:100];
	STAssertEquals([t1 withdrawal], (long long)100, @"Update not processed");
	
	[t1 setDescrip:@"hi"];
	STAssertEquals([t2 descrip], @"hi", @"Update not processed");

	[[[cashbox dataController] managedObjectContext] deleteObject:t1];
	[[[cashbox dataController] managedObjectContext] deleteObject:t2];
	[[cashbox dataController] saveData:nil];
}

@end
